﻿using System;
using System.Globalization;
using System.Linq;

namespace ITSP_Instance_Generator
{
    class Instance
    {
        public Node[] Nodes { get; private set; }
        public int Bound { get; private set; } // the cooldown time
        public double[][] DistanceMatrix { get; private set; }
        public Instance(Node[] nodes, int bound, double[][] distanceMatrix)
        {
            Nodes = nodes;
            Bound = bound;
            DistanceMatrix = distanceMatrix;
        }

        public String ToZIMPL()
        {
            String distanceParam = "param travel_time[nodes*nodes] := ";
            for(int i = 0; i < DistanceMatrix.Length; i++)
                for (int j = 0; j < DistanceMatrix.Length; j++)
                {
                    int i1 = i + 1;
                    int j1 = j + 1;
                    distanceParam += "<" + i1 + "," + j1 + "> " + DistanceMatrix[i][j].ToString(new CultureInfo("en-US", false));
                    if (i1 == DistanceMatrix.Length && j1 == DistanceMatrix.Length)
                        distanceParam += ";";
                    else
                        distanceParam += ", ";
                }

            String nodeSets = "set node_sets[node_sets_indices] := ";
            int nextToBeAdded = 1;
            for (int i = 0; i < Nodes.Count(); i++)
            {
                Node n = Nodes.ElementAt(i);
                String set = "<" + (i+1) + "> {";
                for (int visitNumber = 0; visitNumber < n.NumberOfVisits; visitNumber++)
                {
                    set += nextToBeAdded;
                    nextToBeAdded++;
                    if (visitNumber < n.NumberOfVisits - 1)
                        set += ", ";
                    else
                        set += "}";
                }
                if (i < Nodes.Count() - 1)
                    set += ", ";
                else
                    set += ";";
                nodeSets += set;
            }
            String vi = "set node_sets_indices := { 1 .. " + Nodes.Count() + " };";

            String v = "set nodes := { 1 .. " + DistanceMatrix.Length + " };";
            String variables = @"var path_between[nodes*nodes] binary;
var visited_at[nodes] integer >= 0;";
            String minimizeCost = "minimize cost: visited_at[2];";
            String theRest = @"subto xij_one:
	forall <i> in nodes:
		sum <i,j> in nodes*nodes: path_between[i,j] == 1;

subto xij_two:
	forall <j> in nodes:
		sum <i,j> in nodes*nodes: path_between[i,j] == 1;
subto xii:
	forall <i> in nodes:
		path_between[i,i] == 0;
subto start_time_node_one: visited_at[1] == 0;

subto depot_node: path_between[2,1] == 1; # makes it so that the 2 versions of the depot nodes are connected and that the depot node is the last visited node

subto c:
	forall <i> in nodes:
		forall <j> in nodes without { 1 }:
			1000000*(1-path_between[i,j]) + visited_at[j] >= visited_at[i] + travel_time[i,j];

subto last:
	forall <q> in node_sets_indices:
		forall <i> in node_sets[q]:
			forall <j> in node_sets[q] with i > j:
				visited_at[i] - visited_at[j] >= ";
            string zimpl = vi + Environment.NewLine
                           + nodeSets + Environment.NewLine
                           + v + Environment.NewLine
                           + distanceParam + Environment.NewLine + Environment.NewLine
                           + variables + Environment.NewLine + Environment.NewLine
                           + minimizeCost + Environment.NewLine + Environment.NewLine
                           + theRest + Bound + ";";
            return zimpl;
        }
    }
}
